﻿using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Caching;
using System.Xml.Serialization;
using CY_MVC.Core;
using CY_MVC.Utility;

namespace CY_MVC.UrlRewrite
{
    #region UrlRewriteHelper
    /// <summary>
    /// UrlRewrite工具类
    /// 要在web.config的AppSettings里面设置字段UrlRewriteRulesPath为UrlRewrite规则的路径
    /// </summary>
    public static class UrlRewriteHelper
    {
        public static List<UrlRewriteRule> UrlRewriteRules = new List<UrlRewriteRule>();

        public static void Init()
        {
            if (!string.IsNullOrEmpty(StaticConfig.UrlRewriteRulesPath))
            {
                UrlRewriteRules = XmlDataHelper.LoadFile<List<UrlRewriteRule>>(StaticConfig.UrlRewriteRulesPath) ??
                                  new List<UrlRewriteRule>();
            }
        }

        public static void SetRulesList(IEnumerable<UrlRewriteRule> p_UrlRewriteRules)
        {
            if (p_UrlRewriteRules == null)
            {
                throw new Exception("网址重写列表为空!");
            }
            UrlRewriteRules = new List<UrlRewriteRule>(p_UrlRewriteRules);
            SetRulesList();
        }

        public static void SetRulesList()
        {
            if (UrlRewriteRules == null)
            {
                throw new Exception("网址重写列表为空!");
            }
            XmlDataHelper.SaveFile(UrlRewriteRules, StaticConfig.UrlRewriteRulesPath);
        }

        internal static void Rewrite(object p_Sender, EventArgs p_E)
        {
            var context = ((HttpApplication)p_Sender).Context;
            var ShowUrl = context.Request.Path;
            string RealUrl;
            if (!DataCache.GetCache("CY_MVC.UrlRewrite.UrlRewriteHelper" + ShowUrl.ToLowerInvariant(), out RealUrl))
            {
                if (WebUrl.RegexUrl.IsMatch(ShowUrl))
                {
                    RealUrl = string.Empty;
                    foreach (var item in UrlRewriteRules)
                    {
                        var RegexStr = "^" + WebUrl.ResolveUrl(HttpContext.Current.Request.ApplicationPath, item.ShowUrl.Replace(".", "\\u002E")) + "$";
                        var RegexUrl = new Regex(RegexStr, RegexOptions.IgnoreCase | RegexOptions.Compiled | RegexOptions.Singleline);
                        if (!RegexUrl.IsMatch(ShowUrl)) continue;
                        RealUrl = RegexUrl.Replace(ShowUrl, WebUrl.ResolveUrl(context.Request.ApplicationPath, item.RealUrl));
                        if (item.Stop)
                        {
                            break;
                        }
                    }
                    DataCache.SetCache("CY_MVC.UrlRewrite.UrlRewriteHelper" + ShowUrl.ToLowerInvariant(), RealUrl, Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(30), CacheItemPriority.AboveNormal);
                }
                else
                {
                    DataCache.SetCache("CY_MVC.UrlRewrite.UrlRewriteHelper" + ShowUrl.ToLowerInvariant(), string.Empty, Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(30), CacheItemPriority.AboveNormal);
                }
            }
            if (!string.IsNullOrEmpty(RealUrl))
            {
                RewriteUrl(context, RealUrl);
            }
        }

        /// <summary>
        /// Rewrite's a URL using <b>HttpContext.RewriteUrl()</b>.
        /// </summary>
        /// <param name="p_Context">The HttpContext object to rewrite the URL to.</param>
        /// <param name="p_SendToUrl">The URL to rewrite to.</param>
        internal static void RewriteUrl(HttpContext p_Context, string p_SendToUrl)
        {
            string x, y;
            RewriteUrl(p_Context, p_SendToUrl, out x, out y);
        }

        /// <summary>
        /// Rewrite's a URL using <b>HttpContext.RewriteUrl()</b>.
        /// </summary>
        /// <param name="p_Context">The HttpContext object to rewrite the URL to.</param>
        /// <param name="p_SendToUrl">The URL to rewrite to.</param>
        /// <param name="p_SendToUrlLessQString">Returns the value of sendToUrl stripped of the querystring.</param>
        /// <param name="p_FilePath">Returns the physical file path to the requested page.</param>
        internal static void RewriteUrl(HttpContext p_Context, string p_SendToUrl, out string p_SendToUrlLessQString, out string p_FilePath)
        {
            // see if we need to add any extra querystring information
            if (p_Context.Request.QueryString.Count > 0)
            {
                if (p_SendToUrl.IndexOf('?') != -1)
                    p_SendToUrl += "&" + p_Context.Request.QueryString;
                else
                    p_SendToUrl += "?" + p_Context.Request.QueryString;
            }

            // first strip the querystring, if any
            string queryString = String.Empty;
            p_SendToUrlLessQString = p_SendToUrl;
            if (p_SendToUrl.IndexOf('?') > 0)
            {
                p_SendToUrlLessQString = p_SendToUrl.Substring(0, p_SendToUrl.IndexOf('?'));
                queryString = p_SendToUrl.Substring(p_SendToUrl.IndexOf('?') + 1);
            }

            // grab the file's physical path
            p_FilePath = p_Context.Server.MapPath(p_SendToUrlLessQString);

            // rewrite the path...
            p_Context.RewritePath(p_SendToUrlLessQString, String.Empty, queryString);
        }


        #region 解决虚拟目录的路径问题（暂时移出到别处实现）

        ///// <summary>
        ///// Converts a URL into one that is usable on the requesting client.
        ///// </summary>
        ///// <remarks>Converts ~ to the requesting application path.  Mimics the behavior of the 
        ///// <b>Control.ResolveUrl()</b> method, which is often used by control developers.</remarks>
        ///// <param name="appPath">The application path.</param>
        ///// <param name="url">The URL, which might contain ~.</param>
        ///// <returns>A resolved URL.  If the input parameter <b>url</b> contains ~, it is replaced with the
        ///// value of the <b>appPath</b> parameter.</returns>
        //internal static string ResolveUrl(string appPath, string url)
        //{
        //    if (url.Length == 0 || url[0] != '~')
        //        return url;		// there is no ~ in the first character position, just return the url
        //    else
        //    {
        //        if (url.Length == 1)
        //            return appPath;  // there is just the ~ in the URL, return the appPath
        //        if (url[1] == '/' || url[1] == '\\')
        //        {
        //            // url looks like ~/ or ~\
        //            if (appPath.Length > 1)
        //                return appPath + "/" + url.Substring(2);
        //            else
        //                return "/" + url.Substring(2);
        //        }
        //        else
        //        {
        //            // url looks like ~something
        //            if (appPath.Length > 1)
        //                return appPath + "/" + url.Substring(1);
        //            else
        //                return appPath + url.Substring(1);
        //        }
        //    }
        //}

        #endregion
    }

    #endregion

    #region UrlRewriteRule
    //UrlRewrite规则
    [Serializable]
    [XmlRoot]
    public class UrlRewriteRule
    {
        private string m_ShowUrl;
        private string m_RealUrl;
        private bool m_Stop;

        [XmlAttribute]
        public string ShowUrl
        {
            get { return m_ShowUrl; }
            set { m_ShowUrl = value; }
        }

        [XmlAttribute]
        public string RealUrl
        {
            get { return m_RealUrl; }
            set { m_RealUrl = value; }
        }

        [XmlAttribute]
        public bool Stop
        {
            get { return m_Stop; }
            set { m_Stop = value; }
        }
    }

    #endregion
}
